



#ifndef TRIANGLE_H
#define TRIANGLE_H

#include <windows.h>
#include "3DMath.h"
#include "Texture.h"
#include "SmartPtr.h"
#include "Scan.h"

typedef StaticMatrix<Float3D,5,1> UVPoint3D;

UVPoint3D build_uv_point(Float3D x,Float3D y,Float3D z,Float3D u,Float3D v);
UVPoint3D build_uv_point(const Point3D& pt,Float3D u,Float3D v);
UVPoint3D build_uv_point(const Point3D& pt,const Point2D& uv);
void build_pos_uv_from_full(const UVPoint3D& pt,Point3D* pos,Point2D* uv);

typedef SmartPtr<Texture*> TexturePtr;
typedef Matrix3D UVMatrix;
typedef SmartPtr<UVMatrix*> UVMatrixPtr;

class Triangle
{
public:
	struct TraverseCallBackData
	{
		Point3D pt;
		COLORREF color;
	};
	typedef bool TraverseCallBackFunc(const TraverseCallBackData& data);

	Triangle();
	Triangle(const Point3D& pt1,const Point3D& pt2,const Point3D& pt3,COLORREF color);
	Triangle(const UVPoint3D& pt1,const UVPoint3D& pt2,const UVPoint3D& pt3,TexturePtr texture);

	

	Triangle& transform(const Matrix3D& coordinate);
	template<class TraverseCallBack>
	unsigned int traverse_point(TraverseCallBack& func,const UVMatrix& forward,const UVMatrix& backward)
	{
		unsigned int count = 0;
		TraverseCallBackData data;

		Triangle tri = *this;
		tri.transform(backward);
		tri._build_uv_matrix(NULL);

		m_Scanner.clear();
		m_Scanner.push_back(round(m_Point[0].x()), round(m_Point[0].y()));
		m_Scanner.push_back(round(m_Point[1].x()), round(m_Point[1].y()));
		m_Scanner.push_back(round(m_Point[2].x()), round(m_Point[2].y()));

		int scan_x;
		int scan_y;
		while ( m_Scanner.next(&scan_x, &scan_y) )
		{
			Surface3D sur = build_surface_3D(tri.get_pos_pt(0), tri.get_pos_pt(1), tri.get_pos_pt(2));

			Vector3D N = build_vector_3D(sur(0,0)/sur(3,0),  sur(1,0)/sur(3,0), sur(2,0)/sur(3,0));

			Point3D near_pt = backward * build_point_3D(scan_x,scan_y,0);
			near_pt /= near_pt.h();

			Point3D pa = tri.get_pos_pt(0);
			Float3D t =  (~N * pa)(0,0) / (~N * near_pt)(0,0);

			Point3D real_pt = near_pt * t;
			real_pt(3,0) = 1.0f;

			data.color = tri._get_point_rgb(real_pt);

			data.pt = build_point_3D(scan_x, scan_y, real_pt.z() );
			 
			if ( !func(data) ) 
				return count;

		}


		return count;
	}

	void set_backcolor(COLORREF color);
	void set_pos_pt(unsigned int index,const Point3D& pt);
	void set_uv_pt(unsigned int index,const Point2D& pt);
	void set_full_pt(unsigned int index,const UVPoint3D& pt);
	void set_pos_pt(const Point3D& pt1,const Point3D& pt2,const Point3D& pt3);
	void set_uv_pt(const Point2D& pt1,const Point2D& pt2,const Point2D& pt3);
	void set_full_pt(const UVPoint3D& pt1,const UVPoint3D& pt2,const UVPoint3D& pt3);
	void set_texture(TexturePtr texture)
	{
		m_Texture = texture;
	}

	COLORREF get_backcolor();
	Point3D get_pos_pt(unsigned int index);
	Point2D get_uv_pt(unsigned int index);
	UVPoint3D get_full_pt(unsigned int index);
	TexturePtr get_texture()
	{
		return m_Texture;
	}
	
	void build_uv_matrix(const UVMatrix* additional);

	//bool _traverse_point(Point3D* point_3d, COLORREF* color,  const UVMatrix& forward, const UVMatrix& backward);

	COLORREF get_point_rgb(const Point3D& pt);

private:
	unsigned int _top_point()const;
	unsigned int _bottom_point()const;
	unsigned int _left_point()const;
	unsigned int _right_point()const;
	unsigned int _far_point()const;
	unsigned int _near_point()const;
	

	Point3D m_Point[3];
	Point2D m_Point_UV[3];
	COLORREF m_BackColor;
	TexturePtr m_Texture;
	UVMatrixPtr m_UVMatrix;
	Scan		m_Scanner;
};



#endif